package com.bicycle.model;

import com.bicycle.entity.Point;
import com.bicycle.entity.Route;
import com.bicycle.enums.State;

import java.util.*;

public class Model {

    private int time;
    private Graph graph;
    private People[] peoples;

    public Graph getGraph() {
        return graph;
    }

    /**
     *
     * @param graph 地图数据
     * @param time 运行时间
     */
    public Model(Graph graph,int time){
        this.graph=graph;
        this.time = time;
        this.peoples=new People[time];
    }

    /**
     * 默认图形数据，用于测试
     */
    public Model(){
        //数据输入
        String[] names ={"A站","B站","C站"};
        Point[] points ={
                new Point(0,0),
                new Point(2,3),
                new Point(7,7),
        };
        int[] numbers ={30,40,30};
        int [][]data={
                {2,1,1,1,0,0,0,0},
                {1,0,0,1,0,0,0,0},
                {1,0,0,2,1,1,1,1},
                {1,0,0,0,0,0,0,1},
                {1,1,1,1,1,1,0,1},
                {0,0,0,0,0,1,0,1},
                {0,0,0,0,0,1,1,1},
                {0,0,0,0,0,0,0,2}
        };
        //初始化地图参数
        HashMap<Point, Integer> bicycleNumber = new HashMap<>();
        HashMap< Point,String> hashMap = new HashMap<>();
        HashMap< Point, List<Bicycle>> initialBicycles = new HashMap<>();
        HashMap<Point, Integer> lorryMap = new HashMap<>();
        //初始化地铁站名称和单车数量
        for(int i=0;i<points.length;i++){
            hashMap.put(points[i],names[i]);
            bicycleNumber.put(points[i],numbers[i]);
            lorryMap.put(points[i],0);
        }
        //初始化地铁站单车的编号及位置信息
        int id=0;
        for(int i=0;i<points.length;i++){
            LinkedList<Bicycle> bicycles = new LinkedList<>();
            for(int j=0;j<numbers[i];j++){
                /*
                    =========================
                    =========================
                    重要修改，每一辆单车的point属性应当是不同的对象，因此，对每一辆单车，应该都对应
                    内存当中的一个Point对象
                    =========================
                    =========================
                 */
                Point point = new Point(points[i].getRow(), points[i].getColumn());
                bicycles.add(new Bicycle(id,point));
                id++;
            }
            initialBicycles.put(points[i],bicycles);
        }
        //初始时刻，货车一个停留在a站，另一个在c站
        Lorry[] lorries = new Lorry[2];
        lorries[0]=new Lorry(1,new Point(0,0));
        lorries[1]=new Lorry(2,new Point(2,3));
        this.time=200;
        this.peoples=new People[200];
        this.graph=new Graph(data,hashMap,bicycleNumber,initialBicycles,lorries,lorryMap);
    }

    /**
     * 系统选择单车，更改单车状态
     * @param startPoint 起始位置
     * @param state 单车新状态
     * @param route 路线
     * @return 单车数量充足，返回true
     */
    public boolean choseBicycle(Point startPoint, State state, Route route, Point destination){
        //查看单车数量
        List<Bicycle> bicycles = this.graph.getBicycles().get(startPoint);

        if(bicycles.size() <= 0)return false;
        //随机选择一辆单车
        int bicycleIndex=new Random().nextInt(bicycles.size());
        //修改单车状态
        Bicycle remove = bicycles.remove(bicycleIndex);
        remove.setState(state, route);
        remove.setDestination(destination);
        //将单车加入移动链表
        this.graph.getMovingBicycles().add(remove);
        return true;
    }

    /**
     * 单车控制函数
     * @param time 时刻
     */
    public void bicycleController(int time){

        //获取所有的单车数量
        Map<Point, List<Bicycle>> bicycles = this.graph.getBicycles();
        //获取处于移动状态的车辆
        List<Bicycle> movingBicycles = this.graph.getMovingBicycles();

        //遍历所有的空闲单车
        for (Point point : bicycles.keySet()) {
            List<Bicycle> bicycleList = bicycles.get(point);
            for (int i=0;i<bicycleList.size(); i++){
                bicycleList.get(i).controller(time);
            }
        }
        //暂存需要从movingBicycles中移除德节点
        List<Bicycle> temp = new LinkedList<Bicycle>();
        for (Bicycle movingBicycle : movingBicycles) {
            //到达目的地
            if(movingBicycle.getRoute()!=null&&movingBicycle.getRoute().isArrived()){
                //加入目的地
                movingBicycle.setState(State.IDLE);
                movingBicycle.setRoute(null);
                movingBicycle.setSpeed(0);
                Point destination=movingBicycle.getPoint();
                for (Point point : bicycles.keySet()) {
                    if(point.row==destination.row&&point.column == destination.column){
                        //找到了目的地
                        bicycles.get(point).add(movingBicycle);
                        break;
                    }
                }
                //添加节点
                temp.add(movingBicycle);
            }
            movingBicycle.controller(time);
        }
        //移除
        movingBicycles.removeAll(temp);
    }

    public boolean start(){

        //模拟时间运行
        try{
            for(int i=1;i<=time;i++){

                //在每一个时刻获取小黄车的状态
                this.bicycleController(i);
                //获取货车的状态
                this.lorryController(i);

                //随机生成起始点和终点信息
                Point[] randomPoints = graph.randomPoints();
//                //极端测试，所有的乘客都出现在A地铁站
//                Point[] randomPoints = graph.acSubwayTest();
                //生成测试乘客
                peoples[i-1]=new People(i,randomPoints[0],randomPoints[1]);

                //单车状态需要改变
                Route route = new Route(graph.searchRoute(randomPoints[0], randomPoints[1]));
                this.choseBicycle(randomPoints[0], State.PEOPLE_RID,route,randomPoints[1]);

                //判断是否需要货车移动
                List<Point> points = this.graph.checkTheBicycles();
                for (Point target : points) {
                    //this.graph.getLorryMap().get(target)==0表示第一次分配货车
                    if(target!=null&&this.graph.getLorryMap().get(target)==0){
                        int flag=0;//是否分配货车
                        //找到单车数量最多的地铁站
                        Point start=this.graph.findTheMostBicycles();
                        if(start==target)continue;
                        //确定货车的位置
                        for (Lorry lorry : this.graph.getLorries()) {
                            if(lorry.getState()==State.IDLE&&lorry.getPoint().row==start.row&&lorry.getPoint().column==start.column){
                                //可以直接移动
                                Route stRoute=new Route(this.graph.searchRoute(start,target));
                                lorry.setState(State.MOVING,stRoute);
                                flag=1;
                                //已经分配单车，下一次不再分配
                                this.graph.getLorryMap().put(target,1);
                                //从start地方选取适当的单车，装到货车上
                                for (Map.Entry<Point, List<Bicycle>> entry : this.graph.getBicycles().entrySet()) {
                                    if(entry.getKey().row==start.row&&entry.getKey().column == start.column){
                                        //从此地方开始运送
                                        List<Bicycle> list = entry.getValue();
                                        for(int j=0;j<list.size()/3&&j<20; j++){
                                            this.choseBicycle(start,State.ON_LORRY,copyRoute(stRoute),target);
                                        }
                                    }
                                }
                                break;
                            }
                        }
                        if(flag==0){
                            //需要分配货车，但是货车已经分配完，当货车到达目的地以后，货车将移动到start位置
                            for (Lorry lorry : this.graph.getLorries()) {
                                if(lorry.getState()==State.IDLE){
                                    lorry.setState(State.MOVING,new Route(this.graph.searchRoute(lorry.getPoint(),start)));
                                    System.out.println("货车二次启动----------------------------------------");
                                }
                            }
                        }
                    }

                }


            }
        }catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 输出所有货车在time时刻的状态
     * @param time
     */
    private void lorryController(int time) {
        for (Lorry lorry : this.graph.getLorries()) {
            //货车可能的几种状态
            /*
                1.移动状态
                2.空闲状态
                3.到达目的地，重新变成空闲状态
             */
            lorry.controller(time);
            if(lorry.isArrived()){
                Point point = lorry.getPoint();
                this.graph.getLorryMap().put(point,0);
            }
        }
    }
    private Route copyRoute(Route route){
        List<Point> points=new ArrayList<>();
        for(int i=0;i<route.getNodeAmount();i++){
            Point point = route.getRoute().get(i);
            points.add(i,new Point(point.row,point.column));
        }
        return new Route(points);
    }

}
